package com.liujit.gateway.handler;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.liujit.common.domain.result.ErrorResult;
import com.liujit.common.domain.result.Result;
import com.liujit.common.enums.ResultCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @description: 全局异常处理类
 * @author: liujun
 * @create: 2021/1/4 10:20 上午
 **/
@Slf4j
@Order(-1)
@Configuration
@RequiredArgsConstructor
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {

    private final ObjectMapper objectMapper;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable e) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        if (response.isCommitted()) {
            return Mono.error(e);
        }
        String path = request.getURI().getPath();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Result result;
        if (e instanceof NotFoundException) {
            response.setStatusCode(HttpStatus.NOT_FOUND);
            result = Result.failure(ResultCode.INTERFACE_INNER_INVOKE_ERROR, "Serve Not Found");
        } else if (e instanceof ResponseStatusException) {
            ResponseStatusException responseStatusException = (ResponseStatusException) e;
            response.setStatusCode(responseStatusException.getStatus());
            result = Result.failure(ResultCode.SYSTEM_INNER_ERROR, e.getMessage());
        } else {
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            result = Result.failure(ResultCode.SYSTEM_INNER_ERROR, "Internal Serve Error");
        }
        log.info("GlobalExceptionHandler start, uri:{}, exception:{}, caused by: {}", path, e.getClass(),
                e.getMessage());
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            try {
                return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
            } catch (JsonProcessingException exception) {
                log.error("Error writing response", exception);
                return bufferFactory.wrap(new byte[0]);
            }
        }));
    }
}
